#ifndef LGE_GRAPHICSSUBSYSTEM_H
#define LGE_GRAPHICSSUBSYSTEM_H

#include <core/lge_types.h>
#include <core/lge_subsystem.h>
#include <core/lge_memory.h>

#include <job/lge_jobsubsystem.h>

#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glext.h>

/**
  * \brief Graphics subsystem
  *
  * This manages everything related to graphics. It comes from GPU related
  * tasks, to window management.
  */
class GraphicsSubsystem : public Subsystem<GraphicsSubsystem>
{
public:
	/**
	  * \brief Graphics subsystem configuration structure
	  */
	struct Configuration
	{
		U16 mWidth;
		U16 mHeight;
		bool mVSync;
		bool mFullscreen;

		Configuration(SizeT renderingAffinity=0, U16 width=800, U16 height=600, bool vsync=true, bool fullscreen=false)
		: mWidth(width), mHeight(height), mVSync(vsync), mFullscreen(fullscreen)
		{
		}
	};

private:
	/**
	  * \brief Underlying window handle (OS dept)
	  */
	HWND mMainWindowHandle;

	/**
	  * \brief Underlying device context handle (OS dept)
	  */
	HDC mHdc;

	/**
	  * \brief Used in message pump (OS dept)
	  */
	MSG mMsg;

	/**
	  * \brief Current configuration of the graphics subsystem
	  */
	Configuration mConfiguration;

	/**
	  * \brief Hold a pointer to the JobSubsystem to have an easy access to it
	  */
	JobSubsystem * mJobSubsystem;

	JobState xxxJobState;

	/**
	  * \brief Windows message managing procedure (OS dept)
	  */
	static LRESULT WINAPI windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
	{
		switch (msg)
		{
			// This is called once a user click on the window close button
			case WM_CLOSE:
				break;
			// Default behavior : bypass this windowProc if the message is not
			// handled by the subsystem
			default:
				return DefWindowProc(hwnd, msg, wparam, lparam);
		}

		return 0;
	}

	/**
	  * \brief Create a window and keep all data needed to work with it
	  */
	bool initWindow()
	{
		WNDCLASS wc;
		lgeMemZero<WNDCLASS>(&wc);
		wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
		wc.hInstance = GetModuleHandle(NULL);
		wc.lpszClassName = "LGE";
		wc.lpfnWndProc = windowProc;

		if (!RegisterClass(&wc))
			return false;

		mMainWindowHandle = CreateWindow(
			"LGE",
			"LGE!",
			WS_SYSMENU|WS_CLIPCHILDREN|WS_CLIPSIBLINGS,
			100,
			100,
			mConfiguration.mWidth,
			mConfiguration.mHeight,
			NULL,
			NULL,
			GetModuleHandle(NULL),
			NULL);

		if (!mMainWindowHandle)
			return false;
		
		ShowWindow(mMainWindowHandle, SW_SHOW);
		UpdateWindow(mMainWindowHandle);
		return true;		
	}

	/**
	  * \brief Init OpenGL related things
	  */
	bool initGL()
	{
		mHdc = GetDC(mMainWindowHandle);
		
		PIXELFORMATDESCRIPTOR pfd = {
			sizeof(PIXELFORMATDESCRIPTOR),
			1,
			PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL|PFD_DOUBLEBUFFER,
			PFD_TYPE_RGBA,
			32,
			0,0,0,0,0,0,
			0,0,
			0,0,0,0,0,
			16,
			0,
			0,
			0,
			0,
			0,0,0};

		glClearColor(0.0,0.0,0.0,1.0);
		
		int pixelFormat = ChoosePixelFormat(mHdc,&pfd);
        SetPixelFormat(mHdc,pixelFormat,&pfd);
        HGLRC hglrc = wglCreateContext(mHdc);
        wglMakeCurrent(mHdc,hglrc);

		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluOrtho2D(
			-mConfiguration.mWidth/2,
			mConfiguration.mWidth/2,
			-mConfiguration.mHeight/2,
			mConfiguration.mHeight/2
				);

		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();

		return true;
	}

public:
	/**
	  * \brief Initialize the graphics subsystem
	  */
	bool init(const Configuration & config);

	/**
	  * \brief Called once per frame
	  */
	bool update();
};


#endif
